package cloud.maque.sso.client.controller;

import cloud.maque.common.core.consts.MediaTypes;
import cloud.maque.common.core.enums.LoginType;
import cloud.maque.conf.MaqueProperties;
import cloud.maque.sso.client.annotations.TempToken;
import cloud.maque.sso.client.model.ThridSite;
import cloud.maque.sso.client.service.Oauth2Service;
import cloud.maque.sso.client.utils.SoMap;
import cn.dev33.satoken.stp.StpUtil;
import cn.dev33.satoken.util.SaResult;
import com.alibaba.fastjson2.JSON;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.github.xiaoymin.knife4j.annotations.ApiSupport;
import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.tags.Tag;
import okhttp3.*;
import okhttp3.RequestBody;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.*;

import java.io.IOException;
import java.util.HashMap;
import java.util.Map;
import java.util.Objects;

/**
 * Sa-OAuth2 Client端 控制器
 *
 * @author click33
 */
@ApiSupport(order = 10001)
@RestController
@Tag(name = "SSO客户端接口")
public class SaOAuthClientController {


    @Autowired
    MaqueProperties maqueProperties;

    @Autowired
    Oauth2Service oauth2Service;


    // 根据Code码进行登录，获取 Access-Token 和 openid
    @Operation(summary = "根据Code码进行登录，获取 Access-Token 和 openid")
    @RequestMapping(path = "/codeLogin", method = {RequestMethod.GET, RequestMethod.POST})
    public SaResult codeLogin(@RequestParam("code") String code) {
        // 调用Server端接口，获取 Access-Token 以及其他信息

        OkHttpClient client = new OkHttpClient();

        Map<String, Object> bodyMap = new HashMap<>();

        RequestBody body = RequestBody.create(JSON.toJSONString(bodyMap), MediaTypes.JSON.getMediaType());

        HttpUrl.Builder urlBuilder = Objects.requireNonNull(HttpUrl.parse(maqueProperties.getOauth2().getClient().getServerUrl() + "/oauth2/token")).newBuilder();
        urlBuilder.addQueryParameter("grant_type", "authorization_code");
        urlBuilder.addQueryParameter("code", code);
        urlBuilder.addQueryParameter("client_id", maqueProperties.getOauth2().getClient().getClientId());
        urlBuilder.addQueryParameter("client_secret", maqueProperties.getOauth2().getClient().getClientSecret());
        String url = urlBuilder.build().toString();
        Request request = new Request.Builder()
                .url(url)
                .post(body)
                .build();

        try (Response response = client.newCall(request).execute()) {
            if (response.isSuccessful()) {
                // 处理响应体
                String str = response.body().string();

                System.out.println("返回结果: " + str);
                SoMap so = SoMap.getSoMap().setJsonString(str);

                // code不等于200  代表请求失败
                if (so.getInt("code") != 200) {
                    return SaResult.error(so.getString("msg"));
                }

                // 根据openid获取其对应的userId
                long uid = getUserIdByOpenid(so.getString("openid"));
                so.set("uid", uid);

                // 返回相关参数
                StpUtil.login(uid);
                return SaResult.data(so);
            } else {
                return SaResult.error(response.message());
            }
        } catch (IOException e) {
            return SaResult.error(e.getMessage());
        }
    }

    // 根据 Refresh-Token 去刷新 Access-Token
    @Operation(summary = "根据 Refresh-Token 去刷新 Access-Token")
    @RequestMapping(path = "/refresh", method = {RequestMethod.GET, RequestMethod.POST})
    public SaResult refresh(@RequestParam("refreshToken") String refreshToken) {
        // 调用Server端接口，通过 Refresh-Token 刷新出一个新的 Access-Token

        OkHttpClient client = new OkHttpClient();
        Map<String, Object> bodyMap = new HashMap<>();

        RequestBody body = RequestBody.create(JSON.toJSONString(bodyMap), MediaTypes.JSON.getMediaType());

        HttpUrl.Builder urlBuilder = Objects.requireNonNull(HttpUrl.parse(maqueProperties.getOauth2().getClient().getServerUrl() + "/oauth2/refresh")).newBuilder();
        urlBuilder.addQueryParameter("grant_type", "refresh_token");
        urlBuilder.addQueryParameter("refresh_token", refreshToken);
        urlBuilder.addQueryParameter("client_id", maqueProperties.getOauth2().getClient().getClientId());
        urlBuilder.addQueryParameter("client_secret", maqueProperties.getOauth2().getClient().getClientSecret());
        String url = urlBuilder.build().toString();

        Request request = new Request.Builder().url(url)
                .post(body)
                .build();


        try (Response response = client.newCall(request).execute()) {
            if (response.isSuccessful()) {
                // 处理响应体
                String str = response.body().string();
                SoMap so = SoMap.getSoMap().setJsonString(str);
                System.out.println("返回结果: " + new ObjectMapper().writeValueAsString(so));

                // code不等于200  代表请求失败
                if (so.getInt("code") != 200) {
                    return SaResult.error(so.getString("msg"));
                }
                // 根据openid获取其对应的userId
                long uid = getUserIdByOpenid(so.getString("openid"));
                so.set("uid", uid);

                // 返回相关参数
                StpUtil.login(uid);
                return SaResult.data(so);
            } else {
                return SaResult.error(response.message());
            }
        } catch (IOException e) {
            return SaResult.error(e.getMessage());
        }
    }

    // 模式三：密码式-授权登录
    @Operation(summary = "密码式-授权登录")
    @RequestMapping(path = "/passwordLogin", method = {RequestMethod.GET, RequestMethod.POST})
    public SaResult passwordLogin(@RequestParam("username") String username, @RequestParam("password") String password) {
        // 模式三：密码式-授权登录
        return oauth2Service.PasswordLogin(username, password);
    }


    // 模式四：获取应用的 Client-Token
    @Operation(summary = "获取应用的 Client-Token")
    @RequestMapping(path = "/clientToken", method = {RequestMethod.GET, RequestMethod.POST})
    public SaResult clientToken() {
        // 调用Server端接口
        OkHttpClient client = new OkHttpClient();

        Map<String, Object> bodyMap = new HashMap<>();
        RequestBody body = RequestBody.create(JSON.toJSONString(bodyMap), MediaTypes.JSON.getMediaType());

        HttpUrl.Builder urlBuilder = Objects.requireNonNull(HttpUrl.parse(maqueProperties.getOauth2().getClient().getServerUrl() + "/oauth2/client_token")).newBuilder();
        urlBuilder.addQueryParameter("grant_type", "client_credentials");
        urlBuilder.addQueryParameter("client_id", maqueProperties.getOauth2().getClient().getClientId());
        urlBuilder.addQueryParameter("client_secret", maqueProperties.getOauth2().getClient().getClientSecret());
        String url = urlBuilder.build().toString();

        Request request = new Request.Builder().url(url)
                .post(body)
                .build();


        try (Response response = client.newCall(request).execute()) {
            if (response.isSuccessful()) {
                // 处理响应体
                String str = response.body().string();
                SoMap so = SoMap.getSoMap().setJsonString(str);
                System.out.println("返回结果: " + new ObjectMapper().writeValueAsString(so));

                // code不等于200  代表请求失败
                if (so.getInt("code") != 200) {
                    return SaResult.error(so.getString("msg"));
                }

                // 返回相关参数
                return SaResult.data(so);
            } else {
                return SaResult.error(response.message());
            }
        } catch (IOException e) {
            return SaResult.error(e.getMessage());
        }
    }


    /**
     * 登录并绑定
     * 只接受Post 请求
     *
     * @param username
     * @param password
     * @return
     * @throws JsonProcessingException
     */
    @Operation(summary = "登录并绑定第三方应用")
    @PostMapping("/bind-user")
    @TempToken
    public SaResult LoginAndBind(@RequestParam("username") String username,
                                 @RequestParam("password") String password,
                                 @org.springframework.web.bind.annotation.RequestBody ThridSite thridSite) {
        return oauth2Service.LongAndBind(username, password, thridSite);
    }


    /**
     * 第三方ThridOauth2
     * 该接口不建议公开，因为只用userId就可以实现登录
     *
     * @param thirdType
     * @param thirdUserId
     * @return
     */
    @Operation(summary = "第三方Oauth2,比如微信，钉钉之类")
    @RequestMapping(path = "/third_oauth2", method = {RequestMethod.GET, RequestMethod.POST})
    public SaResult thirdOauth2(@RequestParam("thirdType") String thirdType, @RequestParam("thirdUserId") String thirdUserId) {
        LoginType loginType = LoginType.valueOf(thirdType);
        return oauth2Service.ThrirdGetAccessToken(loginType, thirdUserId);
    }

    /**
     * 发送短信验证码
     *
     * @return
     */
    @Operation(summary = "发送短信验证码")
    @RequestMapping(path = "/send-phone-code", method = {RequestMethod.GET, RequestMethod.POST})
    public SaResult SendPhoneCode(@RequestParam("phone") String phone) {
        return oauth2Service.SendPhoneCode(phone);
    }

    /**
     * 验证码登录
     *
     * @return
     */
    @Operation(summary = "短信验证码登录")
    @RequestMapping(path = "/phone-code-login", method = {RequestMethod.GET, RequestMethod.POST})
    public SaResult PhoneCodeLogin(@RequestParam("phone") String phone, @RequestParam("code") String code) {
        return oauth2Service.PhoneCodeLogin(phone, code);
    }

    /**
     * 时间戳登录
     *
     * @return
     */
    @Operation(summary = "时间验证码登录,比如谷歌，微软验证器")
    @RequestMapping(path = "/totp-login", method = {RequestMethod.GET, RequestMethod.POST})
    public SaResult TotpLogin(@RequestParam("username") String username,
                              @RequestParam("password") String password, @RequestParam(name = "bind", required = false) String bind, @RequestParam(name = "code", required = false) String code) {
        return oauth2Service.TotpLogin(username, password, bind, code);
    }

    // 注销登录
    @Operation(summary = "注销登录")
    @RequestMapping(path = "/logout", method = {RequestMethod.GET, RequestMethod.POST})
    public SaResult logout() {
        StpUtil.logout();
        return SaResult.ok();
    }

    // 根据 Access-Token 置换相关的资源: 获取账号昵称、头像、性别等信息
    @Operation(summary = "根据 Access-Token 获取用户信息")
    @RequestMapping(path = "/getUserinfo", method = {RequestMethod.GET, RequestMethod.POST})
    public SaResult getUserinfo(@RequestParam("accessToken") String accessToken) {
        // 调用Server端接口，查询开放的资源
        OkHttpClient client = new OkHttpClient();

        Map<String, Object> bodyMap = new HashMap<>();
        RequestBody body = RequestBody.create(JSON.toJSONString(bodyMap), MediaTypes.JSON.getMediaType());

        HttpUrl.Builder urlBuilder = Objects.requireNonNull(HttpUrl.parse(maqueProperties.getOauth2().getClient().getServerUrl() + "/oauth2/userinfo")).newBuilder();
        urlBuilder.addQueryParameter("access_token", accessToken);
        String url = urlBuilder.build().toString();
        Request request = new Request.Builder().url(url)
                .post(body)
                .build();


        try (Response response = client.newCall(request).execute()) {
            if (response.isSuccessful()) {
                // 处理响应体
                String str = response.body().string();
                SoMap so = SoMap.getSoMap().setJsonString(str);
                System.out.println("返回结果: " + new ObjectMapper().writeValueAsString(so));

                // code不等于200  代表请求失败
                if (so.getInt("code") != 200) {
                    return SaResult.error(so.getString("msg"));
                }

                // 返回相关参数 (data=获取到的资源 )
                return SaResult.data(so);
            } else {
                return SaResult.error(response.message());
            }
        } catch (IOException e) {
            return SaResult.error(e.getMessage());
        }
    }

    // 全局异常拦截
    @ExceptionHandler
    public SaResult handlerException(Exception e) {
        e.printStackTrace();
        return SaResult.error(e.getMessage());
    }


    // ------------ 模拟方法 ------------------
    // 模拟方法：根据openid获取userId
    private long getUserIdByOpenid(String openid) {
        // 此方法仅做模拟，实际开发要根据具体业务逻辑来获取userId
        return 10001;
    }
}